#include "chatServer.h"
#include <myhead.h>

// 初始化服务器，创建套接字，绑定端口，设置监听
ChatServer::ChatServer(const char *ip, int port, size_t ThreadPoolSize)
    : stop(false)
{
    // 创建套接字
    sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd == -1)
    {
        perror("socket error");
        return;
    }
    // 端口号快速重用
    int reuse = 1;
    if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        perror("setsockopt error");
        return;
    }

    // 绑定端口
    struct sockaddr_in sin;
    bzero(&sin, sizeof(sin));
    sin.sin_family = AF_INET;            // IPv4协议
    sin.sin_port = htons(port);          // 端口号
    sin.sin_addr.s_addr = inet_addr(ip); // IP地址

    if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return;
    }

    // 设置监听
    if (listen(sfd, 10) == -1)
    {
        perror("listen error");
        return;
    }

    // 创建线程池
    startThreadPool(ThreadPoolSize);
}

ChatServer::~ChatServer()
{
    stop = true;          // 设置线程池停止工作
    task_cv.notify_all(); // 通知所有线程退出
    for (auto &worker : workers)
    {
        worker.join(); // 等待线程退出
    }
    close(sfd); // 关闭套接字
}

void ChatServer::startThreadPool(size_t numThreads)
{
    // 循环创建多个线程
    for (int i = 0; i < numThreads; ++i)
    {
        // 创建线程
        workers.emplace_back([this]()
                             {
            while(true)
            {
                function<void()> task;
                {
                    unique_lock<mutex> lock(task_mutex); //获取锁资源,用于保护任务队列
                    task_cv.wait(lock,[this]{
                        return stop || !tasks.empty(); //当线程池停止工作或者任务队列不为空时执行
                    });

                    if(stop && tasks.empty())
                    {
                        return; //线程池停止工作并且任务队列为空,退出线程
                    }

                    task = move(tasks.front()); //获取任务队列的第一个任务
                    tasks.pop(); //弹出任务队列的第一个任务
                }
                task(); //执行任务
            } });
    }
}

void ChatServer::errLog(const char *msg)
{
    cerr << __FILE__ << " " << __func__ << " " << __LINE__ << "\n";
    perror(msg);
}

void ChatServer::addTask(function<void()> task)
{
    {
        unique_lock<mutex> lock(task_mutex); // 获取锁资源,用于保护任务队列
        tasks.push(task);
    }

    task_cv.notify_one(); // 通知一个线程
}

void ChatServer::run() // 运行服务器
{
    // 循环等待客户端连接 有任务时将任务加入任务队列
    while (true)
    {
        struct sockaddr_in cin;
        socklen_t clen = sizeof(cin);

        int client_fd = accept(sfd, (struct sockaddr *)&cin, &clen); // 等待客户端连接
        if (client_fd == -1)
        {
            errLog("accept error");
            continue; // 继续接受
        }

        // 将处理客户端的任务，添加到线程池
        addTask([this, client_fd, cin]
                { handleClient(client_fd, cin); });
    }
}

void ChatServer::handleClient(int client_fd, struct sockaddr_in cin) // 处理客户端请求
{
    MSG msg;                  // 处理后的消息
    char buffer[sizeof(MSG)]; // 接受消息的缓冲区

    // 循环接收客户端消息
    while (true)
    {
        int recv_len = recv(client_fd, buffer, sizeof(buffer), 0); // 接收消息
        if (recv_len <= 0)                                         // 对端下线也是0
        {
            // 表示客户端下线
            auto it = clients.begin();
            while (it != clients.end())
            {
                if (it->fd == client_fd)
                {
                    clients.erase(it);
                    break;
                }
                ++it;
            }
            close(client_fd);
            break;
        }
        // 程序到此表示接收到客户端的消息
        msg.deserialize(string(buffer, recv_len)); // 反序列化消息

        // 对消息类型进行判断
        switch (ntohl(msg.type)) // 网络通信中整形一定要用网络字节序
        {
        case LOGIN:
        {
            // 表示的是客户端发来的是登录消息
            unique_lock<mutex> lock(client_mutex); // 获取锁资源,用于保护客户端列表
            Client new_client;
            new_client.fd = client_fd;
            new_client.cin = cin;

            clients.push_back(new_client); // 添加到客户端列表

            // 组装一条消息，广播给所有客户端
            sprintf(msg.text, "-----%s 进入聊天室-----", msg.name);
            broadcast(msg);
            break;
        }
        case CHAT:
        {
            // 表示的是客户端发来的是聊天消息
            unique_lock<mutex> lock(client_mutex); // 获取锁资源,用于保护客户端列表
            broadcast(msg, client_fd);
            break;
        }
        case QUIT:
        {
            //
            unique_lock<mutex> lock(client_mutex); // 获取锁资源,用于保护客户端列表
            auto it = clients.begin();
            while (it != clients.end())
            {
                if (it->fd == client_fd)
                {
                    clients.erase(it);
                    break;
                }
                ++it;
            }
            sprintf(msg.text, "-----%s 离开聊天室-----", msg.name);
            broadcast(msg);
            
            close(client_fd);
            break;
        }
        default:
            cout << "unknown message type" << endl;
            break;
        }
    }
}

void ChatServer::broadcast(const MSG &msg, int exclude_fd) // 向所有客户端广播消息
{
    // 将要广播的消息序列化成字符串
    string data = msg.serialize();

    // 向所有客户端发送消息
    for (const auto &client : clients)
    {
        if (client.fd == exclude_fd)
        {
            continue;
        }
        if (send(client.fd, data.c_str(), data.size(), 0) == -1)
        {
            errLog("send error");
            return;
        }
    }
}
